#include <GL/glew.h>
#include <GL/glut.h>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <cmath>
#include "world.h"
#include "texture.h"
#include "utility.h"
#include "mesh.h"
#include "shader.h"
#include "context.h"
#include "cursor.h"
#include "select.h"

unsigned range = 0xffffffff;
unsigned WINDOW_WIDTH = 400;
unsigned WINDOW_HEIGHT = 400;

//#define __TEXTURE
#define __SHADER

Mesh mesh ;
Context c ;
Shader shader;

float angle = 0.0;

STATES states; 


void init(void)
{
	//glClearColor(0.0, 0.0, 0.0, 0.0);
	glClearColor(1.0, 1.0, 1.0, 1.0);
	glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_SMOOTH);
	glDepthRange(0.0, 1.0);  /* The default z mapping */
	//Enable texture 

#ifdef __TEXTURE
	makeCheckImage();
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	glGenTextures(1, &texName);
	glBindTexture(GL_TEXTURE_2D, texName);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	glTexImage2D(GL_TEXTURE_2D, 0, 4, checkImageWidth, checkImageHeight, 
				 0, GL_RGBA, GL_UNSIGNED_BYTE, checkImage);
#endif 

}

void drawSence(GLenum mode)
{
	if (mode == GL_SELECT)
		glLoadName(1);
	c.draw_model(); 

}

void unmap_co(unsigned x, unsigned y, unsigned z)
{


	// This function will find 2 points in world space that are on the line into the screen defined by screen-space( ie. window-space ) point (x,y)
	double mvmatrix[16];
	double projmatrix[16];
	int viewport[4];
	double dX, dY, dZ, dClickY; // glUnProject uses doubles, but I'm using floats for these 3D vectors

	double dClickZ = double(z) / range; 
	glGetIntegerv(GL_VIEWPORT, viewport);	
	glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
	glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
	dClickY = double (WINDOW_HEIGHT - y); // OpenGL renders with (0,0) on bottom, mouse reports with (0,0) on top

	//double winx = double(x) / WINDOW_WIDTH;	
	gluUnProject ((double) x, dClickY, dClickZ , mvmatrix, projmatrix, viewport, &dX, &dY, &dZ);
	std::cout<<dX<<' '<<dY<<' '<<dZ<<std::endl;
}

void unproject(unsigned x, unsigned y, unsigned z, float & dx, float & dy, float & dz)
{


	// This function will find 2 points in world space that are on the line into the screen defined by screen-space( ie. window-space ) point (x,y)
	double mvmatrix[16];
	double projmatrix[16];
	int viewport[4];
	double dX, dY, dZ, dClickY; // glUnProject uses doubles, but I'm using floats for these 3D vectors

	double dClickZ = double(z) / range; 
	glGetIntegerv(GL_VIEWPORT, viewport);	
	glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
	glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
	dClickY = double (WINDOW_HEIGHT - y); // OpenGL renders with (0,0) on bottom, mouse reports with (0,0) on top

	//double winx = double(x) / WINDOW_WIDTH;	
	gluUnProject ((double) x, dClickY, dClickZ , mvmatrix, projmatrix, viewport, &dX, &dY, &dZ);
	//std::cout<<dX<<' '<<dY<<' '<<dZ<<std::endl;
	dx = dX; dy = dY ; dz = dZ; 
}

void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_TEXTURE_2D);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
#ifdef GL_VERSION_1_1
	glBindTexture(GL_TEXTURE_2D, texName);
#endif
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	//glTranslatef(0.0f, 0.0f, -2.0f);
	glRotatef(angle, 0.0f, 1.0f, 1.0f); 

	drawSence(GL_RENDER);
	/*
	glBegin(GL_TRIANGLES);
	glColor3ub(251,253,255);
	glVertex3f(1.0f, -0.5f, 0.0f);
	glVertex3f(-1.0f, -0.5f, 0.0f);
	glVertex3f(0.0f, 1.0f, 0.0f);
	glEnd();
	*/
	DrawMouseCursor();
	glFlush();
	glutSwapBuffers();
}

void processNormalKeys(unsigned char key, int x, int y) {

	switch (key)
	{
	case  's' : case 'S':
		if (c.get_sample_state())
		{
			c.unset_samples();
		}
		else
		{
			c.set_samples();
		}
		break;
	case  'u' : case 'U':
		if (states.selected_face_idx > 0)
		{
			mesh.v_faces[states.selected_face_idx - 1]->resolution++;
		}
		break;
	case  'd' : case 'D':
		if (states.selected_face_idx > 0)
		{
			mesh.v_faces[states.selected_face_idx - 1]->resolution--;
			if ( (mesh.v_faces[states.selected_face_idx - 1]->resolution) < 1)
			{
				(mesh.v_faces[states.selected_face_idx - 1]->resolution) = 1;
			}
		}
		break;
	case 'r' : case 'R' :
		angle += 0.2; 
	}
	display();
}

void reshape(int w, int h)
{
	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	//gluPerspective(45.0, 1.0 * w/ h, 0, 1000);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void processPassiveMotion( int x, int y)
{
	
	float dx, dy , dz;
	unproject(x, y, 1.0, dx, dy, dz );
	mousePosition[0] = dx; 
	mousePosition[1] = dy;
	//std::cout<<"Passive dx "<<dx <<" Passive dy"<<dy<<std::endl;
	display();
}
int main(int argc, char **argv)
{

	//std::ifstream ifs("cube3.obj");
	std::ifstream ifs("D:\\workspace\\data\\onetriangle.obj");
	//srand(0);
	ifs >> mesh; 
	std::cout<<mesh.edges.size() <<std::endl;
	c.load_model(&mesh);
	c.rand_face_color(); 
	//c.assign_color(0);
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize (WINDOW_WIDTH, WINDOW_HEIGHT);
	glutInitWindowPosition (100, 100);
	glutCreateWindow(argv[0]);
	init();

	glewInit();
	if (glewIsSupported("GL_VERSION_2_0"))
		printf("Ready for OpenGL 2.0\n");
	else {
		printf("OpenGL 2.0 not supported\n");
		exit(1);
	}

#ifdef WIN32
	shader.load("D:\\workspace\\Brush\\glsl\\mcolor.vert", "D:\\workspace\\Brush\\glsl\\mcolor.frag");
	//shader.load("D:\\workspace\\Brush\\glsl\\mcolor.vert", "D:\\workspace\\Brush\\glsl\\nearest.frag");
#else
	shader.load("glsl/minimal.vert", "glsl/minimal.frag");
#endif

#ifdef __SHADER
	shader.bind();
	shader.set_uniform("draw_face", 1); 
#endif



	glutMouseFunc(select_backbuffer);
	glutPassiveMotionFunc(processPassiveMotion);
	glutKeyboardFunc(processNormalKeys);
	glutReshapeFunc(reshape);
	glutDisplayFunc(display);


	glutMainLoop();
	return 0; 
}
